package com.bdilab.iot.config;

import com.bdilab.iot.common.ResultMap;
import com.bdilab.iot.consts.Constants;
import com.bdilab.iot.dto.userdto.LoginResultDto;
import com.bdilab.iot.enums.HttpCodeEnum;
import com.bdilab.iot.intercepter.JwtTokenFilter;
import com.bdilab.iot.model.User;
import com.bdilab.iot.service.UserService;
import com.bdilab.iot.utils.TokenUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


/**
 * @author WangNing
 * @version 1.0
 * @date 2020/7/31 15:43
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("customerUserDetailService")
    UserDetailsService userDetailsService;

    @Autowired
    private ObjectMapper mapper;

    @Autowired
    private UserService userService;

    @Autowired
    TokenUtils tokenUtils;

    // 密码编码器
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义Jwt过滤器
     * @return
     */
    private JwtTokenFilter jwtTokenFilter(){
        return new JwtTokenFilter(userDetailsService, tokenUtils, userService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .cors().and()
                .authorizeRequests()
                .antMatchers(Constants.BASE_API_PATH +"/login/**").permitAll()
                .antMatchers(Constants.BASE_API_PATH +"/**").authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl(Constants.BASE_API_PATH+"/login")
                .failureHandler(new AjaxAuthenticationFailureHandler()) // 登录失败
                .successHandler(new AjaxAuthenticationSuccessHandler()) // 登录成功
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new RestAuthAccessDeniedHandler())
                .authenticationEntryPoint(new AjaxAuthenticationEntryPoint())
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.NEVER);

        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); // 加入Jwt请求头的过滤器
        http.headers().cacheControl().disable(); // 禁用请求头缓存
    }

    /**
     * 登录失败
     */
    public  class  AjaxAuthenticationFailureHandler implements AuthenticationFailureHandler {
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            ResultMap resultMap = new ResultMap();
            resultMap.fail(HttpCodeEnum.UNAUTHORIZED.getCode()).message("用户名或密码错误");
            response.setStatus(HttpCodeEnum.UNAUTHORIZED.getCode());
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

            mapper.writeValue(
                    response.getWriter(),
                    resultMap
            );

        }
    }

    /**
     * 登录成功
     */
    public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            User user = userService.getUserByUserName(authentication.getName());
            LoginResultDto loginResultDto = new LoginResultDto();
            BeanUtils.copyProperties(user, loginResultDto);
            ResultMap resultMap = new  ResultMap().success(tokenUtils.generateToken(user)).payload(loginResultDto);
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            mapper.writeValue(
                    response.getWriter(),
                    resultMap
            );
        }
    }

    /**
     * 用户已登录但是没有操作权限
     */
    public class RestAuthAccessDeniedHandler implements AccessDeniedHandler {


        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
            ResultMap resultMap = new ResultMap();

            resultMap.fail(HttpCodeEnum.FORBIDDEN.getCode()).message("用户没有操作权限");
            response.setStatus(HttpCodeEnum.FORBIDDEN.getCode());
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            mapper.writeValue(
                    response.getWriter(),
                    resultMap
            );

        }
    }

    /**
     * 用户未登录直接访问接口
     */
    public class AjaxAuthenticationEntryPoint implements AuthenticationEntryPoint{

        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
            ResultMap result = new ResultMap();
            result.fail(HttpCodeEnum.UNAUTHORIZED.getCode()).message("未登录").payload(authException.getMessage());
            response.setStatus(HttpCodeEnum.UNAUTHORIZED.getCode());
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            mapper.writeValue(
                    response.getWriter(),
                    result);
        }
    }

}
